#ifndef ES_IIC_H_
#define ES_IIC_H_


#include "stm_std_lib.h"






//I2C类型
enum I2C_Type { i2c1, i2c2 };



/*
事件模式IIC操作类


I2C1使用PB6作SCL
			  PB7作SDA

I2C2使用PB10(SCL),PB11(SDA)


接法说明：
	IO被设置为GPIO_Mode_AF_OD复用开漏模式，所以SCL与SDA必需分别外接上拉电阻

*/
class CI2C {


public:


	CI2C()
	{
		//指定属性默认值
		DutyCycle = I2C_DutyCycle_2;
		AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
		speed = IIC_SPEED;
	}



	//可编辑属性

	//SCK占空比
	//I2C_DutyCycle_2 或 I2C_DutyCycle_16_9
	uint16_t DutyCycle;


	//i2c地址长度
	//I2C_AcknowledgedAddress_7bit 或 I2C_AcknowledgedAddress_10bit
	uint16_t AcknowledgedAddress;


	//i2c速率
	//100kbps或400kbps
	int speed;




	/*
	初始化
	type: i2c1或i2c2
	address: 7bit i2c本机地址
	*/
	void Init(I2C_Type type, u8 address) {
		this->i2cType = type;

		//硬件配置
		//复位BUSY标记，防止waitBus开机时锁死。此复位操作要放置于，所有I2C寄存器配置之前
		getI2CPointer()->CR1 |= 0x8000;
		getI2CPointer()->CR1 &= ~0x8000;

		RCC_Config();
		GPIO_Config();
		I2C_Config(address);
	}




	/* 数据发送部分 */

	vu8 sendFlag;		//发送完成标志	1=完成

	u8* p_buf;		//缓存
	int bufLen;		//缓存长度
	int sendPtr;		//数据发送指针

	//写地址寄存器
	inline void master_write_reg(u8 slaveAddr, u8 reg, u8 data)
	{
		u8 txdata[2] = { reg, data };

		master_write(slaveAddr, txdata, 2);
	}

	//读取地址寄存器
	inline void master_read_reg(u8 slaveAddr, u8 reg, u8& data)
	{
		master_write_read(slaveAddr, &reg, 1, &data, 1);
	}

	// 		void read16bit_reg(u8 addr, u8 reg, u16* data)
	// 		{
	// 			u8 temp;
	// 			master_read_reg(addr, reg, temp);
	// 			data = 0;
	// 			*data |= temp<<8;
	// 			master_read_reg(addr, reg+1, temp);
	// 			*data |= temp;
	// 		}
			/*
			主机模式-发送数据

			slaveAddr:目标地址(7bit)
			buf:字节缓存起始地址
			len:缓存长度
			*/
	void master_write(u8 slaveAddr, u8* wBuf, int wLen) {
		I2C_TypeDef* pI2C = getI2CPointer();

		//等待总线
		waitBus();

		//开启ACK(?)
		I2C_AcknowledgeConfig(pI2C, ENABLE);

		//建立开始信号
		I2C_GenerateSTART(pI2C, ENABLE);
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_MODE_SELECT)); //等待EV5 (主机模式选择)

		//发送从机地址(方向为发送)
		I2C_Send7bitAddress(pI2C, slaveAddr, I2C_Direction_Transmitter);
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待EV6 (主机发送模式已选中)

		//发送数据
		for (int i = 0; i < wLen; i++) {
			I2C_SendData(pI2C, wBuf[i]);
			while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待EV8 (数据已发送)
		}

		//建立停止信号
		I2C_GenerateSTOP(pI2C, ENABLE);
	}




	/*
	主机模式-先发送数据，然后读取数据

	slaveAddr:目标地址
	wBuf:wirte缓存
	wLen:wirte长度
	rBuf:read缓存
	rLen:read长度
	*/
	void master_write_read(u8 slaveAddr, u8* wBuf, int wLen, u8* rBuf, int rLen) {
		I2C_TypeDef* pI2C = getI2CPointer();

		//等待总线
		waitBus();

		//开启ACK(?)
		I2C_AcknowledgeConfig(pI2C, ENABLE);

		//建立开始信号
		I2C_GenerateSTART(pI2C, ENABLE);
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_MODE_SELECT)); // 等待 EV5 (主模式选择)

		//发送从机地址
		I2C_Send7bitAddress(pI2C, slaveAddr, I2C_Direction_Transmitter);
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待 EV6 (主模式发送选中)

		//发送数据
		for (int i = 0; i < wLen; i++) {
			I2C_SendData(pI2C, wBuf[i]);
			while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待EV8 (数据已发送)
		}



		//转为主机接收模式,不需要STOP。直接重新START
		I2C_GenerateSTART(pI2C, ENABLE);
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_MODE_SELECT)); //等待EV5 (主机模式选择)

		//发送从机地址
		I2C_Send7bitAddress(pI2C, slaveAddr, I2C_Direction_Receiver); //接收方向
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //等待EV6 (主机接收模式选中)


		//接收rLen-1个byte （最后1byte要stop后再接收）
		int ptr = 0;
		for (int i = 0; i < rLen - 1; i++) {
			//等待EV7 (主机-数据已接收)
			while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_BYTE_RECEIVED));
			rBuf[ptr++] = I2C_ReceiveData(pI2C); 		//从I2C寄存器读取接收到的值
		}

		//接收最后1byte之前，关闭ack及建立stop
		//关闭ACK
		I2C_AcknowledgeConfig(pI2C, DISABLE);
		//建立停止信号
		I2C_GenerateSTOP(pI2C, ENABLE);

		//接收最后1byte
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_BYTE_RECEIVED)); //等待EV7 (主机-数据已接收)
		rBuf[ptr++] = I2C_ReceiveData(pI2C); 		//从I2C寄存器读取接收到的值						
	}




	/*
	主机读取模式

	slaveAddr:目标地址
	rBuf:read缓存
	rLen:read长度
	*/
	void master_read(u8 slaveAddr, u8* rBuf, int rLen) {
		I2C_TypeDef* pI2C = getI2CPointer();

		//等待总线
		waitBus();

		//开启ACK(?)
		I2C_AcknowledgeConfig(pI2C, ENABLE);


		//START
		I2C_GenerateSTART(pI2C, ENABLE);
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_MODE_SELECT)); //等待EV5 (主机模式选择)

		//发送从机地址
		I2C_Send7bitAddress(pI2C, slaveAddr, I2C_Direction_Receiver); //接收方向
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //等待EV6 (主机接收模式选中)


		//接收rLen-1个byte （最后1byte要stop后再接收）
		int ptr = 0;
		for (int i = 0; i < rLen - 1; i++) {
			//等待EV7 (主机-数据已接收)
			while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_BYTE_RECEIVED));
			rBuf[ptr++] = I2C_ReceiveData(pI2C); 		//从I2C寄存器读取接收到的值
		}

		//接收最后1byte之前，关闭ack及建立stop
		//关闭ACK
		I2C_AcknowledgeConfig(pI2C, DISABLE);
		//建立停止信号
		I2C_GenerateSTOP(pI2C, ENABLE);

		//接收最后1byte
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_MASTER_BYTE_RECEIVED)); //等待EV7 (主机-数据已接收)
		rBuf[ptr++] = I2C_ReceiveData(pI2C); 		//从I2C寄存器读取接收到的值
	}






	/*
	从机-接收模式(等待直到收到地址匹配)
	返回接收到的字节数
	！如果接收到的字节数超过缓存长度，会立即返回(剩下的将接收不到)

	rBuf:接收缓存
	rLen:接收缓存长度
	*/
	int slave_read(u8* rBuf, int rLen) {
		int rCnt = 0;

		I2C_TypeDef* pI2C = getI2CPointer();

		//开启ACK(?)
		I2C_AcknowledgeConfig(pI2C, ENABLE);


		//等待EV1 (从机接收模式-地址匹配)
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED));


		while (1) {
			//剩下只会接收到EV2或者EV4
			uint32_t evt = I2C_GetLastEvent(pI2C);

			if (evt == I2C_EVENT_SLAVE_BYTE_RECEIVED) {		//EV2 (从机数据已接收)
				rBuf[rCnt++] = I2C_ReceiveData(pI2C);

				//缓存长度检查，防止溢出
				if (rCnt >= rLen)return rCnt;
			}

			if (evt == I2C_EVENT_SLAVE_STOP_DETECTED) {		//EV4 (STOP信号接收到)
				return rCnt;
			}
		}
	}





	/*
	从机-发送模式(等待直到收到地址匹配)
	!如果需要发送的字节数超出缓存长度，将会发出0x0

	wBuf:发送缓存
	wLen:缓存长度
	*/
	void slave_write(u8* wBuf, int wLen) {
		int ptr = 0;

		I2C_TypeDef* pI2C = getI2CPointer();

		//开启ACK(?)
		I2C_AcknowledgeConfig(pI2C, ENABLE);


		//等待EV1 (从机发送模式-地址匹配)
		while (!I2C_CheckEvent(pI2C, I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED));

		//发送第1byte
		I2C_SendData(pI2C, wBuf[ptr++]);


		while (1) {
			//剩下只会接收到EV3或者EV3-2
			uint32_t evt = I2C_GetLastEvent(pI2C);

			if (evt == I2C_EVENT_SLAVE_BYTE_TRANSMITTED) {			//EV3 (从机字节已发送)，表示需要继续发送数据
				//如果要发送的字节数超出缓存长度，发送0
				if (ptr >= wLen) {
					I2C_SendData(pI2C, 0);
				}
				else {
					//否则继续发送					
					I2C_SendData(pI2C, wBuf[ptr++]);
				}
			}

			if (evt == I2C_EVENT_SLAVE_ACK_FAILURE) {			//收到EV3-2 (没有收到ACK，表示不需要再发出数据)
				return;			//结束
			}
		}
	}







	/* 等待I2C总线 */
	void waitBus(void) {
		while (I2C_GetFlagStatus(getI2CPointer(), I2C_FLAG_BUSY));
	}






private:

	I2C_Type i2cType;



	//获取I2C地址
	I2C_TypeDef* getI2CPointer() {
		switch (i2cType) {
		case i2c1:
			return I2C1;

		case i2c2:
			return I2C2;
		}
		return 0;
	}


	void RCC_Config()
	{
		//开启GPIOB时钟
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

		//开启I2C时钟				
		switch (i2cType) {
		case i2c1:
			RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
			break;

		case i2c2:
			RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
			break;
		}
	}






	void GPIO_Config()
	{
		GPIO_InitTypeDef init;

		switch (i2cType) {
		case i2c1:
			init.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
			break;

		case i2c2:
			init.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
			break;
		}

		init.GPIO_Speed = GPIO_Speed_50MHz;
		init.GPIO_Mode = GPIO_Mode_AF_OD;			//开漏输出

		GPIO_Init(GPIOB, &init);
	}









	void I2C_Config(u8 address)
	{
		I2C_InitTypeDef init;

		
		init.I2C_Mode = I2C_Mode_I2C;		//标准I2C模式	
		init.I2C_Ack = I2C_Ack_Enable;				//ack开启



		//可编辑
		init.I2C_DutyCycle = DutyCycle;		//时钟线占空比
		init.I2C_AcknowledgedAddress = AcknowledgedAddress;		//i2c地址长度 (7/10 bit)
		init.I2C_OwnAddress1 = address;			//i2c地址(7/10 bit)
		init.I2C_ClockSpeed = speed;		//速率 (100k /400k)				

		I2C_Init(getI2CPointer(), &init);		//初始化			


		//使能I2C
		I2C_Cmd(getI2CPointer(), ENABLE);
	}




};




#endif
